/**
 * The Free Gaming System game integration API.
 *
 * Copyright 2006 the Free Gaming System developers.
 *
 * FGS is the legal property of its developers, whose names are too
 * numerous to list here.  Please refer to the COPYRIGHT file
 * distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

#ifndef __FGS_H__
#define __FGS_H__

/** The client-server protocol version implemented by this library. */
#define FGS_CLIENT_PROTO_VER 0.2
#define FGS_CLIENT_PROTO_VER_STR "0.2"

/** The list of errors that most of the functions can return. */
typedef enum fgs_error_t {
	FGS_OK = 0,
	FGS_ERR_UNKNOWN,
        FGS_ERR_INVALID_PARAMS,
        FGS_ERR_INVALID_NET_TYPE,
        FGS_ERR_INVALID_CALLBACKS,
	FGS_ERR_INVALID_CONTEXT,
	FGS_ERR_INVALID_PLAYER,
        FGS_ERR_INVALID_MATCH,
        FGS_ERR_REPLY_LINE_TOO_LONG,
        FGS_ERR_MALFORMED_REPLY,
        FGS_ERR_MALFORMED_MAGIC,
        FGS_ERR_CLIENT_PROTO_MISMATCH,
        FGS_ERR_SOCKET_ERROR,
        FGS_ERR_SOCKET_CLOSED,
        FGS_ERR_REGISTRATION_FAILED,
        FGS_ERR_CONFIG_PROCESS,
        FGS_ERR_SHUTDOWN_REQUEST,
        FGS_ERR_UNAUTHORIZED_PLAYER,
        FGS_ERR_UNAUTHORIZED_MATCH,
	FGS_ERR_PROXY_UNREACHABLE,
        FGS_ERR_PLAYER_UNREGISTER_ERROR,
        FGS_ERR_MATCH_UNREGISTER_ERROR,
        FGS_ERR_UPDATE_ERROR,
	FGS_ERR_INTERNAL
} fgs_error_t;

/** An FGS context.
 *
 * This is an opaque structure containing all the
 * information a game needs to provide the FGS library when calling
 * it.
 */
typedef struct fgs_ctx_t fgs_ctx_t;


/** A Universally Unique ID. This type is used throughout the API to
 *  communicate identifiers to the game server.
 */
typedef char* fgs_uuid_t;
#define UUID_ALL NULL


/** A boolean. Can take the values FGS_TRUE or FGS_FALSE. */
typedef enum
  {
    FGS_FALSE = 0,
    FGS_TRUE = 1
  } fgs_bool_t;


/** These baton types are used to pass context into @a fgs_callbacks_t
 *  enabled functions.
 */
typedef void* fgs_baton_t;


/** Callback to read data from a connection object.
 *
 * The callback takes 4 parameters: a baton representing the
 * connection, a pointer to the buffer where the data should be put,
 * and an integer specifying the number of bytes to read. The buffer
 * is guaranteed to be that size. The final parameter is FGS_FALSE if the
 * data should remain on the input queue (ie. still be available for a
 * later read).
 *
 * The callback returns FGS_OK on success, or an error otherwise.
 */
typedef fgs_error_t conn_read_cb_t(fgs_baton_t, char*, int, fgs_bool_t);

/** Callback to write data to a connection object.
 *
 * The callback takes three parameters: a baton representing the
 * connection, a pointer to the data to send, and the amount of data
 * to send.
 *
 * The return value is an fgs_error_t indicating success or failure of
 * the write operation.
 */
typedef fgs_error_t conn_write_cb_t(fgs_baton_t, char*, int);

/** Callback to compare two connection objects.
 *
 * The callback takes the two objects to compare as parameters, and
 * returns FGS_TRUE if they are equal.
 */
typedef fgs_bool_t conn_cmp_cb_t(fgs_baton_t, fgs_baton_t);

/** Callback to modify the configuration of the game server.
 *
 * This callback takes two char* parameters: the key giving the name
 * of the configuration value, and the associated value.
 */
typedef void config_appender_cb_t(char*, char*);

/** FGS callback table.
 *
 * These callbacks are passed into the FGS library during its
 * initialization.
 */
typedef struct fgs_callbacks_t
{
  conn_read_cb_t *read_client; /** Read the requested amount from a
                                   client. */
  conn_write_cb_t *write_client; /** Write data to the client. */
  conn_cmp_cb_t *cmp_client; /** Compare two client batons. */
  config_appender_cb_t *append_config; /** Add the given key/value
                                             pair to the server
                                             config. */
} fgs_callbacks_t;


/** An enumeration of all the builtin communication systems supported
    by the FGS library. */
enum fgs_net_type
  {
    FGS_NONE, /** No pre-configured network type. */
    FGS_TCP,  /** Networking through standard BSD TCP sockets. */
    /* FGS_UDP */
  };


/** An enumeration of all key value types supported by the FGS
 * library.
 */
enum fgs_value_type
  {
    FGS_NIL,    /** No value (== delete). */
    FGS_STRING, /** A char*. */
    FGS_INT,    /** A signed long int. */
    FGS_FLOAT,  /** A double. */
    FGS_BOOL,   /** A @a fgs_bool_t boolean value. */
  };


/** Construct and return a callbacks table.
 *
 * @param cb_table The callbacks table to initialize.
 * @param init_type Initialize the callbacks table with the given
 * network type.
 *
 * @return An initialized callbacks table, or NULL on error.
 */
fgs_error_t
fgs_init_callbacks(fgs_callbacks_t **cb_table,
                   enum fgs_net_type init_type);


/** Initialize the FGS server library.
 *
 * You will need to define and pass in a completed callbacks table, so
 * that the library can use your networking system.
 *
 * @param ctx The context to initialize.
 * @param name The name of the game.
 * @param version The version of the running server.
 * @param server_port The port the game server is listening on.
 * @param cb_table An initialized callbacks table for the game's
 * networking system.
 *
 * @return @a FGS_OK on success, or an error otherwise.
 */
fgs_error_t
fgs_init(fgs_ctx_t **ctx, const char *name, const char *version,
         int server_port, fgs_callbacks_t *cb_table);


/** Shutdown the FGS server library.
 *
 * @param ctx An initialized FGS context to shutdown.
 *
 * @return @a FGS_OK on success, or an error otherwise.
 */
fgs_error_t
fgs_shutdown(fgs_ctx_t *ctx);


/** Process any custom configuration received from the server.
 *
 * @param ctx The FGS context.
 *
 * @return FGS_OK on success, or an error if the processing of the
 * config failed.
 */
fgs_error_t
fgs_process_config(fgs_ctx_t *ctx);


/** Register a new player on the server
 *
 * This function performs player FGS authentication (if possible) and
 * registers the player with the proxy server
 *
 * @param ctx The FGS context.
 * @param uuid The uuid to fill in.
 * @param conn_baton A @a fgs_baton_t describing the network
 * connection.
 * @param bypass_auth FGS_TRUE if the FGS authentication must be
 * bypassed entirely, FGS_FALSE for the standard behaviour: try to
 * authenticate the player in the FGS, and revert to anonymous if it
 * fails.
 *
 * @return @a FGS_OK on success, or an error otherwise.
 */
fgs_error_t
fgs_new_player(fgs_ctx_t *ctx, fgs_uuid_t *uuid,
               fgs_baton_t conn_baton, fgs_bool_t bypass_auth);


/** Register a new player on the server with a TCP transport engine.
 *
 * This function is a wrapper around @a fgs_new_player which should be
 * used when you called fgs_init with the FGS_TCP network transport
 * type.
 *
 * @param ctx The FGS context.
 * @param uuid The uuid to fill in.
 * @param conn_fd The file descriptor representing the connection to
 * the client.
 * @param bypass_auth FGS_TRUE if the FGS authentication must be
 * bypassed entirely, FGS_FALSE for the standard behaviour: try to
 * authenticate the player in the FGS, and revert to anonymous if it
 * fails.
 *
 * @return @a FGS_OK on success, or an error otherwise.
 */
fgs_error_t
fgs_new_player_tcp(fgs_ctx_t *ctx, fgs_uuid_t *uuid,
                   int conn_fd, fgs_bool_t bypass_auth);


/** Unregister a player from the server.
 *
 * If the player is still joined to matches, he is removed from them.
 *
 * @param ctx The FGS context.
 * @param player_uuid The @a fgs_uuid_t of the player to unregister.
 */
fgs_error_t
fgs_remove_player(fgs_ctx_t *ctx, fgs_uuid_t player_uuid);


/** Retrieve a player UUID given the network connection.
 *
 * @param ctx The FGS context.
 * @param uuid The UUID to fill in.
 * @param conn_baton The connection context associated with the
 * player.
 *
 * @return FGS_OK if the UUID was found, or an error otherwise.
 */
fgs_error_t
fgs_get_player(fgs_ctx_t *ctx, fgs_uuid_t *uuid, fgs_baton_t conn_baton);


/** Retrieve a player UUID given the TCP socket descriptor.
 *
 * This function is a wrapper around @a fgs_get_player which should be
 * used when you called fgs_init with the FGS_TCP network transport
 * type.
 *
 * @param ctx The FGS context.
 * @param uuid The uuid to fill in.
 * @param conn_fd The file descriptor representing the connection to
 * the client.
 *
 * @return @a FGS_OK on success, or an error otherwise.
 */
fgs_error_t
fgs_get_player_tcp(fgs_ctx_t *ctx, fgs_uuid_t *uuid, int conn_fd);


/** Register a new match on the server.
 *
 * @param ctx The FGS context.
 * @param uuid The uuid to fill in.
 *
 * @return @a FGS_OK on success, or an error otherwise.
 */
fgs_error_t
fgs_new_match(fgs_ctx_t *ctx, fgs_uuid_t *uuid);


/** End the given match on the server.
 *
 * @param ctx The FGS context.
 * @param match_uuid The @a fgs_uuid_t of the match to end.
 *
 * @return @a FGS_OK on success, or an error otherwise.
 */
fgs_error_t
fgs_end_match(fgs_ctx_t *ctx, fgs_uuid_t match_uuid);


/** Provide information about a match.
 *
 * The information is provided as a key/value pair associated with a
 * match uuid.  If the key already exists, its value is replaced.  The
 * variable argument field should contain a single argument: the value
 * associated with the key, cast to the type given by the @a
 * value_type argument.
 *
 * @param ctx The FGS context.
 * @param match_uuid The @a fgs_uuid_t of the match being updated.
 * @param key The name of the key being updated
 * @param value_type A @a fgs_value_type giving the type of the value.
 *
 * @return @a FGS_OK on success, or an error otherwise.
 */
fgs_error_t
fgs_update_match(fgs_ctx_t *ctx, fgs_uuid_t match_uuid, char *key,
                 enum fgs_value_type value_type, ...);


/** Provide information about a player.
 *
 * The information is provided as a key/value pair associated with a
 * player uuid.  If the key already exists, its value is replaced.  The
 * variable argument field should contain a single argument: the value
 * associated with the key, cast to the type given by the @a
 * value_type argument.
 *
 * @param ctx The FGS context.
 * @param player_uuid The @a fgs_uuid_t of the player being updated.
 * @param key The name of the key being updated
 * @param value_type A @a fgs_value_type giving the type of the value.
 *
 * @return @a FGS_OK on success, or an error otherwise.
 */
fgs_error_t
fgs_update_player(fgs_ctx_t *ctx, fgs_uuid_t player_uuid, char *key,
                  enum fgs_value_type value_type, ...);


/** Connect a player to a match.
 *
 * Provide explicit notification to the FGS that the given player is
 * now operating on the given match.  If you do not provide this
 * explicit notification, the library sends it when you set the first
 * value on a variable of the (match, player) tuple.
 *
 * @param ctx The FGS context.
 * @param match_uuid The @a fgs_uuid_t of the match the player is joining.
 * @param player_uuid The @a fgs_uuid_t of the player joining the match.
 *
 * @return @a FGS_OK on success, or an error otherwise.
 */
fgs_error_t
fgs_join_match(fgs_ctx_t *ctx, fgs_uuid_t match_uuid, fgs_uuid_t player_uuid);


/** Disconnect a player from a match.
 *
 * Provide explicit notification to the FGS that the given player is
 * no longer operating on the given match.  If you do not provide this
 * explicit notification, the player stays connected to the match
 * until the match ends.
 *
 * @param ctx The FGS context.
 * @param match_uuid The @a fgs_uuid_t of the match the player is leaving.
 * @param player_uuid The @a fgs_uuid_t of the player leaving the match.
 *
 * @return @a FGS_OK on success, or an error otherwise.
 */
fgs_error_t
fgs_leave_match(fgs_ctx_t *ctx, fgs_uuid_t match_uuid, fgs_uuid_t player_uuid);


/** Propagate all the stored match information to the backend.
 *
 * Until you call this function, the proxy stores all the @a
 * fgs_update_match and @a fgs_update_match_player in a local cache.
 * When you call it, the proxy atomically propagates all the updates
 * to the backend.
 *
 * If match_uuid is the special UUID_ALL value, then propagate all
 * information for all matches to the backend.  Atomicity is only
 * guaranteed per-match, not for the entire propagation.
 *
 * @param ctx The FGS context.
 * @param match_uuid The @a fgs_uuid_t of the match to propagate.  If
 *  it is UUID_ALL, propagate all matches to the backend.
 *
 * @return @a FGS_OK on success, or an error otherwise.
 */
fgs_error_t
fgs_commit_match(fgs_ctx_t *ctx, fgs_uuid_t match_uuid);


/** Propagate all the stored player information to the backend.
 *
 * Until you call this function, the proxy stores all the @a
 * fgs_update_player in a local cache.  When you call it, the proxy
 * atomically propagates all the updates to the backend.
 *
 * If player_uuid is the special UUID_ALL value, then propagate all
 * information for all players to the backend.  Atomicity is only
 * guaranteed per-player, not for the entire propagation.
 *
 * @param ctx The FGS context.
 * @param player_uuid The @a fgs_uuid_t of the player to propagate.  If
 *  it is UUID_ALL, propagate all players to the backend.
 *
 * @return @a FGS_OK on success, or an error otherwise.
 */
fgs_error_t
fgs_commit_player(fgs_ctx_t *ctx, fgs_uuid_t player_uuid);


/** Provide match information about a player.
 *
 * The information is provided as a key/value pair associated with a
 * player and match uuid.  If the key already exists, its value is
 * replaced.  The variable argument field should contain a single
 * argument: the value associated with the key, cast to the type given
 * by the @a value_type argument.
 *
 * @param ctx The FGS context.
 * @param match_uuid The @a fgs_uuid_t of the match being updated.
 * @param player_uuid The @a fgs_uuid_t of the player being updated.
 * @param key The name of the key being updated
 * @param value_type A @a fgs_value_type giving the type of the value.
 *
 * @return @a FGS_OK on success, or an error otherwise.
 */
fgs_error_t
fgs_update_match_player(fgs_ctx_t *ctx, fgs_uuid_t match_uuid,
                        fgs_uuid_t player_uuid, char *key,
                        enum fgs_value_type value_type, ...);


/** Return the string associated with an error code.
 *
 * Exceptionally, as this function is stateless, it does not take a
 * context handle.  Don't forget to free the returned string when
 * you're done with it!
 *
 * @param err_code The @fgs_error_t giving the error code.
 *
 * @return The error string, dynamically allocated.
 */
char *
fgs_strerror(fgs_error_t err_code);

#endif /* __FGS_H__ */
